/*
Copyright [2012] [Maria Eugenia Malagamba]
Copyright [2012] [Leandro Pellegrino]
Copyright [2012] [Julia Gambuzzi]
Copyright [2012] [Ignacio Capodanno]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "ParserXML.h"

using namespace Parser;

ParserXML::ParserXML(string carpetaCliente){
	this->documento = new TiXmlDocument();
	this->carpetaCliente = carpetaCliente;
	this->validador = new ValidadorXML(this->documento, carpetaCliente);
	this->log = Log::getInstanciaValidador();
	this->log->escribir("==================================");
	this->log->escribir("   Logs validador xml ASTEROIDS");
	this->log->escribir("==================================");
	this->vectorTiposAsteroide = new vector<string>();
	this->configuracionPorDefecto = new ConfiguracionPorDefecto();
}

bool ParserXML::parsearDocumento(string path)
{
	bool loadOk = this->documento->LoadFile(path.c_str(), TIXML_ENCODING_UTF8);
	
	if (loadOk){
		this->validarDocumento();
		return true;
	}else{
		this->log->escribir("ERROR: Error al parsear el documento " + path);
		this->documento->LoadFile("asteroidsPorDefecto.xml", TIXML_ENCODING_UTF8);
		return false;
	}
}


/* Valida los tags y los atributos.
 * Si no se encuentra un tag agrega uno por defecto
 * Si algun atributo no se encuentra o es inválido se coloca uno por defecto.
 * Retorna 'true' si todo está bien */
void ParserXML::validarDocumento()
{
	chequearTagAsteroids();
	chequearTagTiposAsteroide();
	chequearTiposAsteroides();
	chequearTagAsteroides();
	chequearAsteroides();
	chequearTagNave();
}

void ParserXML::chequearTagAsteroids()
{
	TiXmlElement *asteroids = this->documento->FirstChildElement("asteroids");
	
	this->log->escribir("INFO: Chequeando Tag Asteroids.");
	this->validador->chequearNodo(NULL, asteroids, "asteroids");
	this->validador->chequearAtributosTagAsteroids(asteroids);
	this->validador->chequearAtributoEntero(asteroids, "alto", this->configuracionPorDefecto->getAltoAsteroids());
	this->validador->chequearAtributoEntero(asteroids, "ancho", this->configuracionPorDefecto->getAnchoAsteroids());
	this->validador->chequearAltoAnchoPantalla(asteroids);
}

void ParserXML::chequearTagTiposAsteroide()
{
	TiXmlElement *padre = this->documento->FirstChildElement("asteroids");
	TiXmlElement *tiposAsteroide = padre->FirstChildElement("tiposAsteroide");
	
	this->log->escribir("INFO: Chequeando Tag Tipos de Asteroides.");
	this->validador->chequearNodo(padre, tiposAsteroide, "tiposAsteroide");

}

void ParserXML::chequearTiposAsteroides()
{
	TiXmlElement *padre = this->documento->FirstChildElement("asteroids")->FirstChildElement("tiposAsteroide");
	TiXmlElement *tipoAsteroide = padre->FirstChildElement("tipoAsteroide");

	this->log->escribir("INFO: Chequeando Tag Tipo de Asteroides.");
	
	while (tipoAsteroide != 0){
		this->validador->chequearAtributosTagTipoAsteroide(tipoAsteroide);
		
		if(esTipoAsteroideNoRepetido(tipoAsteroide) && tipoAsteroideNoExcedePantalla(tipoAsteroide)){
			this->validador->chequearAtributoString(tipoAsteroide, "nombre", this->configuracionPorDefecto->getTipoDeAsteroide());
			this->validador->chequearAtributoImagen(tipoAsteroide, "imagen", this->configuracionPorDefecto->getImagenDeAsteroide());
			this->validador->chequearAtributoEntero(tipoAsteroide, "ancho", this->configuracionPorDefecto->getAnchoDeTipoDeAsteroide());
			this->validador->chequearAtributoEntero(tipoAsteroide, "alto", this->configuracionPorDefecto->getAltoDeTipoDeAsteroide());
			this->validador->chequearAtributoEntero(tipoAsteroide, "rotacion", this->configuracionPorDefecto->getRotacionDeTipoDeAsteroide());

			//TODO ver si el casteo anda
			this->vectorTiposAsteroide->push_back((string)tipoAsteroide->Attribute("nombre"));
		}
		tipoAsteroide = tipoAsteroide->NextSiblingElement();
	}
	agregarTipoAsteroidePorDefecto();
}

void ParserXML::chequearTagAsteroides()
{
	TiXmlElement *padre = this->documento->FirstChildElement("asteroids");
	TiXmlElement *tiposAsteroide = padre->FirstChildElement("asteroides");
	
	this->log->escribir("INFO: Chequeando Tag Asteroides.");
	this->validador->chequearNodo(padre, tiposAsteroide, "asteroides");
}

void ParserXML::chequearAsteroides()
{
	int contadorAsteroides = 0;
	TiXmlElement *padre = this->documento->FirstChildElement("asteroids")->FirstChildElement("asteroides");
	TiXmlElement *asteroide = padre->FirstChildElement("asteroide");

	this->log->escribir("INFO: Chequeando Asteroides.");
	while (asteroide != 0)
	{
		this->validador->chequearAtributosTagAsteroide(asteroide);
		string tipo = asteroide->Attribute("tipo");

		this->validador->chequearAtributoString(asteroide, "tipo", this->configuracionPorDefecto->getTipoDeAsteroide());
		if(!Buscador::buscarValorEnVector(this->vectorTiposAsteroide, tipo)){
			asteroide->SetAttribute("tipo", this->configuracionPorDefecto->getTipoDeAsteroide().c_str());
			this->log->escribir("ERROR: El tipo de asteroide no está definido. Se usara el tipo por defecto.", 
				asteroide->Row(), asteroide->Column()); 
		}
		this->validador->chequearAtributoEntero(asteroide, "x", this->configuracionPorDefecto->getXAsteroide());
		this->validador->chequearAtributoEntero(asteroide, "y", this->configuracionPorDefecto->getYAsteroide());
		this->validador->chequearAtributoEntero(asteroide, "direccion", this->configuracionPorDefecto->getDireccionAsteroide());
		this->validador->chequearAtributoEntero(asteroide, "velocidad", this->configuracionPorDefecto->getVelocidadAsteroide());

		//si está fuera de la pantalla le pongo un tipo inválida para que el hidratador no lo cargue
		int alto = atoi(this->documento->FirstChildElement("asteroids")->Attribute("alto"));
		int ancho = atoi(this->documento->FirstChildElement("asteroids")->Attribute("ancho"));
		if(!this->validador->chequearAsteoideDentroPantalla(alto, ancho, asteroide)){
			asteroide->SetAttribute("tipo", "|");
			this->log->escribir("ERROR: El asteroide está definido fuera de la pantalla y no se pintará", 
				asteroide->Row(), asteroide->Column()); 
		}else{
			contadorAsteroides++;
		}
		
		asteroide = asteroide->NextSiblingElement();
	}
	chequearQueHayAsteroides(contadorAsteroides);

}
	
void ParserXML::chequearTagNave()
{
	TiXmlElement *padre = this->documento->FirstChildElement("asteroids");
	TiXmlElement *nave = padre->FirstChildElement("nave");

	this->log->escribir("INFO: Chequeando Tag Nave.");
	this->validador->chequearNodo(padre, nave, "nave");
	this->validador->chequearAtributosTagNave(nave);
	this->validador->chequearAtributoImagen(nave, "imagen", this->configuracionPorDefecto->getImagenNave());
	this->validador->chequearAtributoDouble(nave, "velRotacion", this->configuracionPorDefecto->getVelocidadDeRotacionNave());
	this->validador->chequearAtributoEntero(nave, "velMaxima", this->configuracionPorDefecto->getVelocidadMaximaNave());
	this->validador->chequearAtributoEntero(nave, "propulsion", this->configuracionPorDefecto->getPropulsionNave());
	this->validador->chequearAtributoEntero(nave, "friccion", this->configuracionPorDefecto->getFriccionNave());
	this->validador->chequearAtributoEntero(nave, "velocidadBala", this->configuracionPorDefecto->getVelocidadBalaNave());
	this->validador->chequearAtributoEntero(nave, "duracionBala", this->configuracionPorDefecto->getDuracionBalaNave());
	this->validador->chequearAtributoEntero(nave, "ciclosParaReparar", this->configuracionPorDefecto->getCiclosParaReparar());
}

bool ParserXML::esTipoAsteroideNoRepetido(TiXmlElement *tipoAsteroide){
	string nombreTipoAsteroide = tipoAsteroide->Attribute("nombre");
	if(Buscador::buscarValorEnVector(this->vectorTiposAsteroide, nombreTipoAsteroide)){
		tipoAsteroide->SetAttribute("nombre","|");
		this->log->escribir("ERROR: Se encontro un tipo de asteroide repetido", 
			tipoAsteroide->Row(), tipoAsteroide->Column());
		return false;
	}else{
		return true;
	}
}

//si el tamanio excede la pantalla, descartamos el tipo
bool ParserXML::tipoAsteroideNoExcedePantalla(TiXmlElement *tipoAsteroide){
	TiXmlElement *asteroids = this->documento->FirstChildElement("asteroids");
	int anchoPantalla = atoi(asteroids->Attribute("ancho"));
	int altoPantalla = atoi(asteroids->Attribute("alto"));
	
	if(!this->validador->chequearTamanioAsteroide(altoPantalla, altoPantalla, tipoAsteroide)){
		tipoAsteroide->SetAttribute("nombre","|");
		this->log->escribir("ERROR: Se encontro un tipo de asteroide con tamanio mas grande que la pantalla", 
			tipoAsteroide->Row(), tipoAsteroide->Column());
		return false;
	}else{
		return true;
	}
}

/*si no hay ningun asteroide valido en el xml, cargo 5 del tipo por 
* defecto o dos de cada tipo existente
*/
void ParserXML::chequearQueHayAsteroides(int contadorAsteroides){
	
	TiXmlElement *asteroids = this->documento->FirstChildElement("asteroids");
	srand(time(NULL));
	int anchoPantalla = atoi(asteroids->Attribute("ancho"));
	int altoPantalla = atoi(asteroids->Attribute("alto"));
	int x = rand()%(anchoPantalla);
	int y = rand()%(altoPantalla);
	int dir = 73;
	int vel = 64;

	TiXmlElement *asteroides = this->documento->FirstChildElement("asteroids")->FirstChildElement("asteroides");
	if(contadorAsteroides == 0){
		if(this->vectorTiposAsteroide->size() == 1){
			int i;
			for(i = 0; i<5; i++){
				TiXmlElement *asteroide = new TiXmlElement("asteroide");
				asteroide->SetAttribute("tipo", this->vectorTiposAsteroide->at(0).c_str());
				asteroide->SetAttribute("x", x);
				asteroide->SetAttribute("y", y);
				asteroide->SetAttribute("direccion", dir);
				asteroide->SetAttribute("velocidad", vel);
				asteroides->LinkEndChild(asteroide);
				x = rand()%(anchoPantalla);
				y = rand()%(altoPantalla);
				dir = dir+x;
				vel = vel++;
			}
		}else{
			for(int i = 0; i < this->vectorTiposAsteroide->size(); i++){
				TiXmlElement *asteroide = new TiXmlElement("asteroide");
				asteroide->SetAttribute("tipo", this->vectorTiposAsteroide->at(i).c_str());
				asteroide->SetAttribute("x", x);
				asteroide->SetAttribute("y", y);
				asteroide->SetAttribute("direccion", dir);
				asteroide->SetAttribute("velocidad", vel);
				asteroides->LinkEndChild(asteroide);
				x = rand()%(anchoPantalla);
				y = rand()%(altoPantalla);
				dir = dir+x;
				vel = vel++;
				asteroide = new TiXmlElement("asteroide");
				asteroide->SetAttribute("tipo", this->vectorTiposAsteroide->at(i).c_str());
				asteroide->SetAttribute("x", x);
				asteroide->SetAttribute("y", y);
				asteroide->SetAttribute("direccion", dir);
				asteroide->SetAttribute("velocidad", vel);
				asteroides->LinkEndChild(asteroide);
				x = rand()%(anchoPantalla);
				y = rand()%(altoPantalla);
				dir = dir+x;
				vel = vel++;

			}
		}
	}
}

void ParserXML::agregarTipoAsteroidePorDefecto(){
	TiXmlElement *tiposAsteroide = this->documento->FirstChildElement("asteroids")->FirstChildElement("tiposAsteroide");
	TiXmlElement *tipoAsteroidePorDefecto = new TiXmlElement("tipoAsteroide");
	tipoAsteroidePorDefecto->SetAttribute("nombre", this->configuracionPorDefecto->getTipoDeAsteroide().c_str());
	tipoAsteroidePorDefecto->SetAttribute("imagen", this->configuracionPorDefecto->getImagenDeAsteroide().c_str());
   	tipoAsteroidePorDefecto->SetAttribute("ancho", this->configuracionPorDefecto->getAnchoDeTipoDeAsteroide().c_str());
	tipoAsteroidePorDefecto->SetAttribute("alto", this->configuracionPorDefecto->getAltoDeTipoDeAsteroide().c_str());
	tipoAsteroidePorDefecto->SetAttribute("rotacion", this->configuracionPorDefecto->getRotacionDeTipoDeAsteroide().c_str());
	tiposAsteroide->LinkEndChild(tipoAsteroidePorDefecto);
	this->vectorTiposAsteroide->push_back(this->configuracionPorDefecto->getTipoDeAsteroide());
}

TiXmlDocument *ParserXML::getDocumento()
{
	return this->documento;
}

ParserXML::~ParserXML(void)
{
	delete this->documento;
	delete this->validador;
	//delete this->log;
	delete this->vectorTiposAsteroide;
	delete this->configuracionPorDefecto;
}